package com.zs.grain.service

import android.content.Context
import android.os.Build
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.util.valueIterator
import com.jeremyliao.liveeventbus.LiveEventBus
import com.zs.grain.device.*
import com.xnkj1688.semplibrary.entity.*
import com.xnkj1688.semplibrary.ulti.*
import com.zs.grain.BuildConfig
import com.zs.grain.tool.clockManage.LocalDataBase
import com.zslibrary.BusMaster.BusMaster
import com.zslibrary.Enum.ProducingStatusDefine
import io.reactivex.Single
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.SingleSubject
import java.io.File
import java.lang.reflect.InvocationTargetException
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.collections.ArrayList
import kotlin.concurrent.schedule

/**
 * Created by Wesley on 2017/7/15.
 */

@Singleton
class DeviceService @Inject constructor(
    val mContext: Context,
    val mDataService: DataService,
    val mSignalRService: SignalRService
) : INfcListener {

    private var lastCardNumber: String? = null;
    override fun onSwipeCard(cardNumber: String) {
        if (lastCardNumber == null) {
            lastCardNumber = cardNumber
            this.sendSwipeCardMessage(cardNumber);
            Single.just("")
                .subscribe({
                    Thread.sleep(2 * 1000)
                    lastCardNumber = null
                }, {})
        }
    }

    var _stateTimer: Timer? = null
    var state: String = ""
        get() = field
        private set(value) {
            if (field == value) return
            field = value
            if (field == "") return

            mDataService.insertLog(field)
            var array = field.split(':')
            if (array.size < 2) return

            this._stateTimer?.cancel()
            this._stateTimer = Timer()
            this._stateTimer?.schedule(1000, 5000) {
                mSignalRService.uploadDeviceState(array[0], array[1])
                    .subscribe({ this.cancel() }, {})
            }
        }

    var isReady: Boolean = false
        get() = field
        private set(value) {
            if (field == value) return
            field = value
            this.sendStateMessage()
        }

    var isMachineInit: Boolean = false
        get() = field
        private set(value) {
            if (field == value) return
            field = value
            this.sendInitMessage()
        }

    var isProForgotTake: Boolean = false
        get() = field
        private set(value) {
            if (field == value) return
            field = value
            this.sendRemotePushMessage()
        }

    val errors: Array<Pair<EnumError, Boolean>>
        get() {
            if (mDataService.isEmulator) return arrayOf()
            return mGrainDevice.errors
        }
    var lastError: String = ""
    val errorsString: String
        get() {
            var errorBuilder = StringBuilder()
            if (mGrainDevice.isThereGateDepleted) {
                errorBuilder.append("杯未准备好", ",\t")
                //this.fillCup()//TODO:error handler
            }
            for (item in errors) {
                val errString = mContext.getString(item.first.value)
                lastError = errString
                errorBuilder.append(errString, ",\t")
            }

            return errorBuilder.toString()
        }

    val unvaliableComponents
        get() = mGrainDevice.unvaliableComponents

    val isHotting
        get() = if (mDataService.isEmulator) true else mGrainDevice.isHotting

    lateinit var mGrainDevice: GrainDevice
    lateinit var mMcuDevice: McuDevice

    val hardwareVersion: String
        get() {
            if (mDataService.isEmulator) return "emulator"
            return mGrainDevice.hardwareVersion
        }

//    val vt = mGrainDevice?.getSoftwareVersion(0,1)
//    var ioAppVersion = vt.ver1
//    var ioBootVersion = vt.ver2
    val ioAppVersion
        get() = if (mDataService.isEmulator) "emulator" else mGrainDevice.ioAppVersion
    val ioBootVersion
        get() = if (mDataService.isEmulator) "emulator" else mGrainDevice.ioBootVersion
    val cupAppVersion
        get() = if (mDataService.isEmulator) "emulator" else mGrainDevice.cupAppVersion
    val cupBootVersion
        get() = if (mDataService.isEmulator) "emulator" else mGrainDevice.cupBootVersion
    val craftVersion
        get() = if (mDataService.isEmulator) "emulator" else getCraftName()//mGrainDevice.craftVersion

    val components: List<Component>
        get() {
            var components = mGrainDevice.components
            var list = ArrayList<Component>()
            if (components != null) {
                for (item in components.valueIterator()) {
                    var component = Component()
                    component.id = item.id.toLong()
                    component.name = item.name
                    component.ingridentType = item.property
                    list.add(component)
                }
            }
            return list
        }

    private var mDisposable: Disposable? = null

    init {
        mGrainDevice = GrainDevice(mContext)
        mMcuDevice = McuDevice(mContext)
    }

    fun getCraftName():String{
        val fileNames: MutableList<String> = mutableListOf()
        //在该目录下走一圈，得到文件目录树结构
        val fileTree: FileTreeWalk = File(mDataService.craftPath).walk()
        fileTree.maxDepth(1) //需遍历的目录层次为1，即无须检查子目录
            .filter { it.isFile } //只挑选文件，不处理文件夹
//        .filter { it.extension == "txt"  } //选择扩展名为txt的文本文件
            .filter { it.extension in listOf("json") }//选择扩展名为txt或者mp4的文件
            .filter { it.name.contains("zsRcp") }
            .forEach { fileNames.add(it.name) }//循环 处理符合条件的文件
        //deleteIfNoUnique(filePath,"LocalAdjustments.json")

        if(!fileNames.isEmpty())
            return fileNames.find {  x -> x.contains("zsRcp") }!!
        else return "---无---"
    }

    fun init() {
        val deviceName = mDataService.deviceName
        val adjustments = mDataService.adjustments
        //if (!mDataService.isEmulator) {
            mGrainDevice.open(deviceName, mDataService.craftPath, adjustments.toTypedArray())
            mMcuDevice.init()
        //}

        mMcuDevice.setNfcListenter(this);

        mDisposable?.dispose()
        mDisposable = SingleSubject.create<Unit>() {
            try {
                Thread.sleep(1000)
                startBeatHeart(mDisposable!!)
            } catch (e: Exception) {
                Log.e(TAG, "stop beatheart：" + e.message)
            }
        }.subscribeOn(Schedulers.io())
            .subscribe({ }, { it.printStackTrace() })
    }

    private fun startBeatHeart(disposable: Disposable) {
        while (disposable == mDisposable && !disposable.isDisposed) {
            var tick = (10 * 1000).toLong()
            val lastTick = 0L//device.lastActiveTick
            if (System.currentTimeMillis() - lastTick >= tick) {
                try {
                    this.beatHeart()
                } catch (e: Exception) {
//                    e.printStackTrace()
                }
            }
            if (lastTick == 0L) tick = (2 * 1000).toLong()
            else tick -= (System.currentTimeMillis() - lastTick)
            Thread.sleep(Math.max(0, tick))
        }
    }

    fun mcuOpenDoor() = mMcuDevice.mcuOpenDoor()

    fun mcuCloseDoor() = mMcuDevice.mcuCloseDoor()
    var hasFillCup = false
    var isCupError = false
    var appInfoHasUpdate = false
    var mAppInfo: MachineInfo? = null
    fun beatHeart(): Boolean {
        if (mDataService.isEmulator) {
            Thread.sleep(1 * 1000)
            this.isReady = true
            this.state = "Info:设备正常"
            return true
        }
//        return true;
        var ready = true
        if (mGrainDevice.isThereGateDepleted) {
            ready = false
        }

        if (errors.find { x -> x.second } != null) {
            ready = false
            if(errorsString.contains("缺杯")){
                isCupError = true
            }else{
                isCupError = false
                hasFillCup = false
            }
        }else{
            isCupError = false
            hasFillCup = false
        }
        //if(errorsString.contains("缺水")) this.fillWater()
        if(isCupError&&!hasFillCup) {
            Single.just(1)
                .subscribeOn(Schedulers.io())
                .subscribe({
                    this.fillCup()
                    hasFillCup = true
                }, {
                    it.printStackTrace()
                })
        }

        if(isMachineInit && appInfoHasUpdate==false){
            Single.just(1)
                .subscribeOn(Schedulers.io())
                .subscribe({
                    mAppInfo = this.getVersionInfo(mContext);
                    if (mAppInfo != null) mSignalRService.uploadMachineInfo(mAppInfo!!)
                    this.appInfoHasUpdate = true
                }, {
                    it.printStackTrace()
                })
        }

        //2020.2.28 jerry 实时刷新产品是否可售
        LiveEventBus.get("RefreshMenu").post(true)


        //2020.2.28 jerry 获取IO板初始化状态
        if(BusMaster.getInstance(mContext).isMachineInit) {
            //this.appInfoHasUpdate = true
            this.isMachineInit = true
            ready = false
        }else this.isMachineInit = false

        this.isReady = ready
        var info = this.errorsString

        //TODO:产品未取走判断
        if(info.contains("产品未取走") && !this.isReady )
            this.isProForgotTake = true
        else
            this.isProForgotTake = false

        if (ready && info == "") {
            this.state = "Info:设备正常"
            mMcuDevice.mcuLightControl(EnumLightStatus.NORMAL)
        } else if (!ready && info == "") {
            this.state = "Info:设备初始化"//"Info:设备准备初始化"
            mMcuDevice.mcuLightControl(EnumLightStatus.INIT)
        } else if (ready && info != "") {
            this.state = "Warning:$info"
            mMcuDevice.mcuLightControl(EnumLightStatus.WARNING)
        } else {
            this.state = "Error:$info"
            mMcuDevice.mcuLightControl(EnumLightStatus.ERROR)
        }
        return ready
    }

    fun getVersionInfo(context: Context): MachineInfo {
        val info = MachineInfo()
        info.id = AppUtil.getUniqueID(context);
        info.apiLevel = Build.VERSION.SDK_INT
        info.applicationId = context.applicationInfo.processName
        val packageInfo =
            context.packageManager.getPackageInfo(context.packageName, 0)
        info.version = packageInfo.versionName + "." + packageInfo.versionCode
        info.flavor = com.facebook.drawee.BuildConfig.FLAVOR;

        if (ActivityCompat.checkSelfPermission(
                context,
                "android.permission.READ_PHONE_STATE"
            ) == 0
        ) {
            val telephonyManager =
                context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
            info.IMEI = telephonyManager.deviceId
            info.IMSI = telephonyManager.subscriberId
            info.ICCID = telephonyManager.simSerialNumber
            if (Build.VERSION.SDK_INT >= 22) {
                val subInfo =
                    SubscriptionManager.from(context).getActiveSubscriptionInfoForSimSlotIndex(0)
                if (subInfo != null) info.ICCID = subInfo.iccId
            }
            try {
                val method =
                    telephonyManager.javaClass.getDeclaredMethod("getSubscriberInfo")
                try {
                    method.isAccessible = true
                    val obj = method.invoke(telephonyManager)
                    val method2 =
                        obj.javaClass.getDeclaredMethod("getPhone", Integer.TYPE)
                    method2.isAccessible = true
                    val obj2 = method2.invoke(obj, 0)
                    val method3 =
                        obj2.javaClass.getMethod("getFullIccSerialNumber")
                    info.ICCID = method3.invoke(obj2) as String
                } catch (var11: IllegalAccessException) {
                    var11.printStackTrace()
                } catch (var12: InvocationTargetException) {
                    var12.printStackTrace()
                }
            } catch (var13: NoSuchMethodException) {
                var13.printStackTrace()
            }
        }
        var attachments = arrayListOf<String>()
        attachments.add("IoAppVersion:${this.ioAppVersion}")
        attachments.add("IoBootVersion:${this.ioBootVersion}")
        attachments.add("CupAppVersion:${this.cupAppVersion}")
        attachments.add("CupBootVersion:${this.cupBootVersion}")
        attachments.add("CraftVersion:${this.craftVersion}")
        info.attachments = attachments.toTypedArray()
        return info;
    }

    fun getTestInfos(componentId: Int) = mGrainDevice.getTestInfos(componentId)

    fun testComponent(componentId: Int, funId: Int) = mGrainDevice.testComponent(componentId, funId)

    fun saveCalibration(componentyId: Int, times: Int, value: Int): Adjustment {
        var adjustment = mGrainDevice.saveCalibration(componentyId, times, value)
        mDataService.save(adjustment)
        return adjustment
    }

    fun saveAdjustment(adjustment: Adjustment) {
        mDataService.save(adjustment)
        var adjustments = mDataService.adjustments
        mGrainDevice.setAllUserAdjustments(adjustments.toTypedArray())
    }

    fun setAdjustments() {
        var adjustments = mDataService.adjustments
        if (!mDataService.isEmulator)
            mGrainDevice.setAllUserAdjustments(adjustments.toTypedArray())
    }

    fun getAdjustments(componentId: Int): Array<AdjustBean> =
        mGrainDevice.getAdjustments(componentId)

    fun makeProduct(goodId: Long): Deliver {
        return this.makeProduct(goodId, "")
    }

    fun makeProduct(goodId: Long, adjustReceipe: String?): Deliver {
        var deliver = Deliver().apply { this.goodId = goodId }
        var receipes = mDataService.getReceipes(goodId)
        receipes = this.mergeReceipes(receipes, adjustReceipe)

        var craftId = mDataService.getCraftId(goodId)!!
        if (mDataService.isEmulator) {
            Thread.sleep(5 * 1000)
            deliver.isSuccess = false
            sendTakingMessage()
        } else deliver.isSuccess = this.makeProduct(receipes, craftId,goodId)
        if(deliver.isSuccess) mDataService.insertLog("Product:产品制作成功")//TODO:new log
        //else mDataService.insertLog("产品制作失败")
        return deliver
    }

    fun mergeReceipes(
        receipes: MutableList<Receipe>,
        adjustReceipe: String?
    ): MutableList<Receipe> {
        var array = adjustReceipe?.split(';')
        if (array == null) return receipes
        for (item in array) {
            var arr = item.split(":")
            if (arr.size < 2) continue
            try {
                var id = arr[0].toInt()
                var dosage = arr[1].toInt()
                var receipe = receipes.find { x -> x.ingridentId == id }
                if (receipe == null) {
                    receipe = Receipe()
                    receipe.ingridentId = id
                    receipes.add(receipe)
                }
                receipe.std = dosage
            } catch (e: Throwable) {
                e.printStackTrace()
            }
        }
        return receipes
    }

    fun checkProductAvaliable(goodId: Long): Boolean {
        val receipes = mDataService.getReceipes(goodId)
        for (item in receipes) {
            var component = getAvaliableComponent(item.ingridentId)
            if (component == null)
                return false
        }
        return true
    }

    fun getAvaliableComponent(ingrident: Int): Component? {
        val unvaliableComponents = this.unvaliableComponents

//        if(this.unvaliableComponents.size>=1)
//            Log.d("zs_pro",this.unvaliableComponents[0].toString())
//        else
//            Log.d("zs_pro","no unvaliable");
        var components = mDataService.getComponents(ingrident)
        for (item in components) {
            if (unvaliableComponents.find { x -> x == item.id.toInt() } == null) {
                return item
            }
        }
        return null
    }

    fun makeProduct(receipes: List<Receipe>, craftId: Int,goodId:Long): Boolean {
        var list = ArrayList<ReceipeBean>()
        var components = ArrayList<Component>()
        for (item in receipes) {
            var receipe = ReceipeBean()
            var component = this.getAvaliableComponent(item.ingridentId)
            if (component == null) continue// throw Exception("原料不足")
            receipe.componentId = component.id.toInt()
            receipe.dosage = item.std
            receipe.property = component.ingridentType
            list.add(receipe)
            component.count -= item.std
            mDataService.update(component)
        }
        mMcuDevice.mcuLightControl(EnumLightStatus.ERROR)
        mDataService.insertLog("Product:开始制作产品:"+goodId.toString())
        var result = mGrainDevice.makeProduct(list.toTypedArray(), craftId)
//        if (!result) mDataService.insertLog("发送制作指令失败")

       return  result//(result && this.producingStatus != EnumProducingStatus.PRODUCING)
    }

    fun make(order: Order): Boolean {
        var result = true
        for (detail in order.details) {
            val deliverCount = if (detail.deliverCount == null) 0 else detail.deliverCount
            val count = detail.count - deliverCount
            for (i in 1..count) {
                var deliver = this.makeProduct(detail.goodId, detail.remark)
                val time = DateUtil.format(System.currentTimeMillis(), "yyMMddHHmmss")
                deliver.id = java.lang.Long.parseLong(time) * 1000000 + mDataService.terminalId
                deliver.orderId = order.id
                deliver.orderDetailNo = detail.no
                order.delivers.add(deliver)
                mDataService.DaoDeliver.insert(deliver)
                //mDataService.insertLog("State:产品订单上传成功")
                if (detail.deliverCount == null) detail.deliverCount = 0
                if (deliver.isSuccess ?: false) {
                    detail.deliverCount += 1
                    mDataService.DaoOrderDetail.update(detail)
                    sendFinishMessage()
                } else {
                    break
                }
            }
            if (detail.deliverCount < detail.count) {
                result = false
                break
            }
        }
        sendCompletedMessage()
        return result
    }
    var isMakingProduct:Boolean = false
        get() = field
        set(value) {
            if (field == value) return
            field = value
            mDataService.insertLog("isMakingProuct:"+field.toString())
            //this.sendInitMessage()
        }

    var isMaintenance = false
    val producingProgress
        get() = mGrainDevice.producingProgress
    val producingStatus
        get() = mGrainDevice.producingStatus
    val updateProgress: Int
        get() = mGrainDevice.updateProgress

    fun openBigDoor() = this.testComponent(2001, 0)
    fun openSmallDoor() = this.testComponent(5801, 0)
    fun fillCup() = this.testComponent(5301, 1)
    fun rushT() = this.testComponent(2001, 6)
    fun distinctT() = this.testComponent(2001, 7)
    fun fillWater() = this.testComponent(5501, 5)
    fun dynTest() = this.testComponent(0, 0)
    fun clearAllError() = mGrainDevice.clearAllError()
    fun takeProduct() = this.testComponent(5801, 4)
    fun updateIoApp(fileName: String) {
        this.appInfoHasUpdate = false
//        var fileName = mDataService.craftPath + "/IOBOARD_V2.0.07.STD.bin";
        mGrainDevice.updateSoftware(EnumBoard.IO, EnumBoardType.APP, fileName)
    }

    fun updateIoBoot(fileName: String) {
        this.appInfoHasUpdate = false
//        var fileName = mDataService.craftPath + "/IOBOARD_V2.0.07.STD.bin";
        mGrainDevice.updateSoftware(EnumBoard.IO, EnumBoardType.BOOT, fileName)
    }

    fun updateCupApp(fileName: String) {
        this.appInfoHasUpdate = false
//        var fileName = mDataService.craftPath + "/CUPBOARD_V2.0.03.bin";
        mGrainDevice.updateSoftware(EnumBoard.CUP, EnumBoardType.APP, fileName)
    }

    fun updateCupBoot(fileName: String) {
        this.appInfoHasUpdate = false
//        var fileName = mDataService.craftPath + "/CUPBOARD_V2.0.03.bin";
        mGrainDevice.updateSoftware(EnumBoard.CUP, EnumBoardType.BOOT, fileName)
    }

    fun sendInitMessage() = LiveEventBus.get("DeviceService.IsInit").post(isReady)
    fun sendStateMessage() = LiveEventBus.get("DeviceService.IsReady").post(isReady)
    fun sendTakingMessage() = LiveEventBus.get("DeviceService.Taking").post(true)
    fun sendFinishMessage() = LiveEventBus.get("DeviceService.Finish").post(true)
    fun sendErrorMessage(error: String) = LiveEventBus.get("DeviceService.Error").post(error)
    fun sendCompletedMessage() = LiveEventBus.get("DeviceService.Completed").post(true)
    fun sendSwipeCardMessage(cardNumber: String) {//=
        if (!isMaintenance)
            LiveEventBus.get("DeviceService.SwipeCard").post(cardNumber)
    }
    fun sendRemotePushMessage() = LiveEventBus.get("DeviceService.IsRemotePush").post(isReady)
    companion object {
        private val TAG = "DeviceService"
    }
}
